#include <benchmark/benchmark.h>
#include <memory>

/**
 * @brief 简单结构体，用于测试智能指针操作成本。
 *        使用小对象可以突出智能指针自身的管理开销，而不会被对象构造成本淹没。
 */
struct Dummy {
    int value = 42;
};

/**
 * @brief 评估 std::make_unique 创建与销毁对象的平均耗时。
 *
 * N：每次基准迭代内部，构造/析构的次数。
 * 核心目的：衡量 unique_ptr 的构造、析构成本（无控制块、无原子操作）。
 */
static void BM_UniquePtrConstructDestroy(benchmark::State& state) {
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            auto ptr = std::make_unique<Dummy>();
            benchmark::DoNotOptimize(ptr);
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_UniquePtrConstructDestroy)
    ->Arg(100'000);

/**
 * @brief 评估 std::make_shared 创建与销毁对象的平均耗时。
 *
 * 与 unique_ptr 相比，shared_ptr 需要：
 *   1. 分配控制块
 *   2. 初始化引用计数（通常是原子类型）
 *
 * 通过对比得出 shared_ptr 的额外成本。
 */
static void BM_SharedPtrConstructDestroy(benchmark::State& state) {
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            auto ptr = std::make_shared<Dummy>();
            benchmark::DoNotOptimize(ptr);
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_SharedPtrConstructDestroy)
    ->Arg(100'000);

/**
 * @brief 评估 shared_ptr 拷贝成本，并增加多线程场景。
 *
 * 多线程处理方式：
 *   - 使用函数内静态变量，使所有线程共享同一个控制块，从而触发原子引用计数的竞争。
 *   - 使用 ThreadRange 注册多组线程数：1/2/4/8/16（指数增长），也可换成 Threads(1), Threads(16) 明确指定。
 *
 * 注意：
 *   - 如果想测试“每个线程有各自独立的 shared_ptr 并 copy 自己的指针”，可将 static 改为局部变量。
 *   - 这里使用 Arg(1'000'000) 控制每个线程每次迭代要执行的 copy 次数，以放大差异。
 */
static void BM_SharedPtrCopy(benchmark::State& state) {
    // static 保证所有线程共享同一控制块。
    static std::shared_ptr<Dummy> shared_src = std::make_shared<Dummy>();

    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            auto copy = shared_src;          // 原子引用计数 +1
            benchmark::DoNotOptimize(copy);
        }
    }

    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_SharedPtrCopy)
    ->Arg(1'000'000)
    // ->Threads(1)->Threads(16);    // 若想只测试单线程和 16 线程，可用这种链式写法。
    ->ThreadRange(1, 16)            // 推荐：自动测试 1、2、4、8、16 线程。
    ->UseRealTime();                // 多线程下更关注 wall-clock 时间（避免 CPU 时间统计偏差）。

/**
 * @brief 评估 unique_ptr 移动构造的成本。
 *
 * unique_ptr 的移动仅传递所有权（指针值），没有引用计数或控制块，需要的工作极少。
 */
static void BM_UniquePtrMove(benchmark::State& state) {
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            auto src = std::make_unique<Dummy>();
            auto dst = std::move(src);
            benchmark::DoNotOptimize(dst);
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_UniquePtrMove)
    ->Arg(1'000'000);

/**
 * @brief 评估 shared_ptr 移动构造的成本。
 *
 * 注意：即使“移动”，shared_ptr 仍需调整控制块，通常涉及原子操作。
 * 这里仍以单线程测试为主（多线程移动场景较少见，如需可以自行添加 ->ThreadRange）。
 */
static void BM_SharedPtrMove(benchmark::State& state) {
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            auto src = std::make_shared<Dummy>();
            auto dst = std::move(src);
            benchmark::DoNotOptimize(dst);
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_SharedPtrMove)
    ->Arg(1'000'000);

/**
 * @brief 评估 unique_ptr 的 reset 成本。
 *
 * reset(new Dummy) → 接管新对象
 * reset()         → 释放托管对象
 *
 * 该测试主要用来对比 shared_ptr 的 reset 多出的引用计数、控制块清理成本。
 */
static void BM_UniquePtrReset(benchmark::State& state) {
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            std::unique_ptr<Dummy> ptr;
            ptr.reset(new Dummy);
            benchmark::DoNotOptimize(ptr);
            ptr.reset();
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_UniquePtrReset)
    ->Arg(1'000'000);

/**
 * @brief 评估 shared_ptr 的 reset 成本，并增加可选多线程测试。
 *
 * reset(new ...) 会创建新的控制块；reset() 则会减少引用计数并在计数归零时释放对象与控制块。
 * 若要观测多线程下控制块竞争成本，可添加 ThreadRange。
 */
static void BM_SharedPtrReset(benchmark::State& state) {
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            std::shared_ptr<Dummy> ptr;
            ptr.reset(new Dummy);
            benchmark::DoNotOptimize(ptr);
            ptr.reset();
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
// 这里演示两种写法（二选一使用即可）：
BENCHMARK(BM_SharedPtrReset)
    ->Arg(1'000'000)
    ->Threads(1)        // 单线程
    ->Threads(16)       // 多线程（如果想看更多线程数，可改用 ThreadRange）
    ->UseRealTime();

/**
 * @brief 评估 unique_ptr 的解引用开销。
 *
 * 基本上等价于裸指针的解引用，属于 CPU 测试下限，差异往往在测量噪声以内。
 */
static void BM_UniquePtrDereference(benchmark::State& state) {
    auto ptr = std::make_unique<Dummy>();
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            benchmark::DoNotOptimize(ptr->value);
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_UniquePtrDereference)
    ->Arg(10'000'000);

/**
 * @brief 评估 shared_ptr 的解引用开销。
 *
 * 与 unique_ptr 类似，解引用本身的成本极低，非常接近测量噪声。
 */
static void BM_SharedPtrDereference(benchmark::State& state) {
    auto ptr = std::make_shared<Dummy>();
    const int N = static_cast<int>(state.range(0));
    for (auto _ : state) {
        for (int i = 0; i < N; ++i) {
            benchmark::DoNotOptimize(ptr->value);
        }
    }
    state.SetItemsProcessed(static_cast<int64_t>(state.iterations()) * N);
}
BENCHMARK(BM_SharedPtrDereference)
    ->Arg(10'000'000);

/**
 * @brief Google Benchmark 入口宏。
 *
 * BENCHMARK_MAIN() 会自动生成 main 函数，运行以上所有注册的基准。
 */
BENCHMARK_MAIN();